1.정리
  • 관계가 없는 테이블간의 조인시 카테시안 곱만큼의 로우가 생성된다.
  • 1이란 숫자는 아무리 여러 번을 곱하여도 1이라는 수학적인 성질을 이용하여
    참조해야 할 여러 개의 테이블간의 연결을 하나의 SQL로 합칠 수가 있을 것이다.
  • SQL의 별도 수행은 잦은 DBMS 호출을 초래하여 시스템 오버헤드의 주범이 된다.
  • 조인은 어느 한 집합만 공집합이 되더라도 전체가 공집합이 되어 버리므로
    다른 성공한 집합까지 실패하게 된다.
  • SQL에서 OUTER 조인을 하고자 하는 임의의 조인 컬럼에 반드시 성공하는 집합의 컬럼을
    결과에 영향을 미치지 않도록 가공하여 추가하면 어느 한 집합이 공집합이 되더라도
    수행결과는 정상적으로 추출된다.
2.테이블 설계

(1)고객(CUSTOMER)
고객번호(CUST_NO/PK), 고객명(CUST_NAME)

(2)사원(EMPLOYEE)
사원번호(EMP_NO/PK), 부서(DEPT), 호봉(SALEGRADE)

(3)예산(BUDGET)
부서(DEPT_NO/PK1), 계정과목(ACCOUNT/PK2), 예산년도(BUDGET_YR/PK3), 편성금액(BUDGET_AMT)

3.CREATE TABLE

SQL>

CREATE TABLE ORA9.CUSTOMER (

2 CUST_NO VARCHAR2(4) NOT NULL

3 , CUST_NAME VARCHAR2(10) NOT NULL

4 )

5 TABLESPACE ORA9;

테이블이 생성되었습니다.

SQL>

COMMENT ON TABLE ORA9.CUSTOMER IS '고객';

주석이 생성되었습니다.

SQL>

COMMENT ON COLUMN ORA9.CUSTOMER.CUST_NO IS '고객번호';

주석이 생성되었습니다.

SQL>

COMMENT ON COLUMN ORA9.CUSTOMER.CUST_NAME IS '고객명';

주석이 생성되었습니다.

SQL>

ALTER TABLE ORA9.CUSTOMER ADD (

2 CONSTRAINT CUSTOMER_PK PRIMARY KEY ( CUST_NO )

3 USING INDEX TABLESPACE ORA9 );

테이블이 변경되었습니다.

SQL>

CREATE TABLE ORA9.EMPLOYEE (

2 EMP_NO VARCHAR2(4) NOT NULL

3 , DEPT VARCHAR2(10) NOT NULL

4 , SALEGRADE VARCHAR2(2) NOT NULL

5 )

6 TABLESPACE ORA9;

테이블이 생성되었습니다.

SQL>

COMMENT ON TABLE ORA9.EMPLOYEE IS '사원';

주석이 생성되었습니다.

SQL>

COMMENT ON COLUMN ORA9.EMPLOYEE.EMP_NO IS '사원번호';

주석이 생성되었습니다.

SQL>

COMMENT ON COLUMN ORA9.EMPLOYEE.DEPT IS '부서';

주석이 생성되었습니다.

SQL>

COMMENT ON COLUMN ORA9.EMPLOYEE.SALEGRADE IS '호봉';

주석이 생성되었습니다.

SQL>

ALTER TABLE ORA9.EMPLOYEE ADD (

2 CONSTRAINT EMPLOYEE_PK PRIMARY KEY ( EMP_NO )

3 USING INDEX TABLESPACE ORA9 );

테이블이 변경되었습니다.

SQL>

CREATE TABLE ORA9.BUDGET (

2 DEPT_NO VARCHAR2(4) NOT NULL

3 , ACCOUNT VARCHAR2(4) NOT NULL

4 , BUDGET_YR VARCHAR2(4) NOT NULL

5 , BUDGET_AMT NUMBER(10) NOT NULL

6 )

7 TABLESPACE ORA9;

테이블이 생성되었습니다.

SQL>

COMMENT ON TABLE ORA9.BUDGET IS '예산';

주석이 생성되었습니다.

SQL>

COMMENT ON COLUMN ORA9.BUDGET.DEPT_NO IS '부서';

주석이 생성되었습니다.

SQL>

COMMENT ON COLUMN ORA9.BUDGET.ACCOUNT IS '계정과목';

주석이 생성되었습니다.

SQL>

COMMENT ON COLUMN ORA9.BUDGET.BUDGET_YR IS '예산년도';

주석이 생성되었습니다.

SQL>

COMMENT ON COLUMN ORA9.BUDGET.BUDGET_AMT IS '편성금액';

주석이 생성되었습니다.

SQL>

ALTER TABLE ORA9.BUDGET ADD (

2 CONSTRAINT BUDGET_PK PRIMARY KEY ( DEPT_NO, ACCOUNT, BUDGET_YR )

3 USING INDEX TABLESPACE ORA9 );

테이블이 변경되었습니다.


4.INSERT

SQL>

INSERT INTO ORA9.CUSTOMER VALUES('0001','고객1');

1 개의 행이 만들어졌습니다.

SQL>

INSERT INTO ORA9.CUSTOMER VALUES('0002','고객2');

1 개의 행이 만들어졌습니다.

SQL>

INSERT INTO ORA9.CUSTOMER VALUES('0003','고객3');

1 개의 행이 만들어졌습니다.

SQL>

INSERT INTO ORA9.CUSTOMER VALUES('0004','고객4');

1 개의 행이 만들어졌습니다.

SQL>

INSERT INTO ORA9.CUSTOMER VALUES('0005','고객5');

1 개의 행이 만들어졌습니다.

SQL>

COMMIT;

커밋이 완료되었습니다.

SQL>

INSERT INTO ORA9.EMPLOYEE VALUES('0001','개발','1');

1 개의 행이 만들어졌습니다.

SQL>

INSERT INTO ORA9.EMPLOYEE VALUES('0002','영업','2');

1 개의 행이 만들어졌습니다.

SQL>

INSERT INTO ORA9.EMPLOYEE VALUES('0003','마케팅','1');

1 개의 행이 만들어졌습니다.

SQL>

INSERT INTO ORA9.EMPLOYEE VALUES('0004','경영','3');

1 개의 행이 만들어졌습니다.

SQL>

INSERT INTO ORA9.EMPLOYEE VALUES('0005','지원','1');

1 개의 행이 만들어졌습니다.

SQL>

COMMIT;

커밋이 완료되었습니다.

SQL>

INSERT INTO ORA9.BUDGET VALUES('0001','1101','2009',3000000);

1 개의 행이 만들어졌습니다.

SQL>

INSERT INTO ORA9.BUDGET VALUES('0001','1101','2008',2500000);

1 개의 행이 만들어졌습니다.

SQL>

INSERT INTO ORA9.BUDGET VALUES('0001','1102','2009',3500000);

1 개의 행이 만들어졌습니다.

SQL>

INSERT INTO ORA9.BUDGET VALUES('0001','1102','2008',2800000);

1 개의 행이 만들어졌습니다.

SQL>

INSERT INTO ORA9.BUDGET VALUES('0002','1101','2009',2500000);

1 개의 행이 만들어졌습니다.

SQL>

INSERT INTO ORA9.BUDGET VALUES('0002','1101','2008',2000000);

1 개의 행이 만들어졌습니다.

SQL>

INSERT INTO ORA9.BUDGET VALUES('0002','1102','2009',4500000);

1 개의 행이 만들어졌습니다.

SQL>

INSERT INTO ORA9.BUDGET VALUES('0002','1102','2008',3600000);

1 개의 행이 만들어졌습니다.

SQL>

INSERT INTO ORA9.BUDGET VALUES('0003','1101','2009',3200000);

1 개의 행이 만들어졌습니다.

SQL>

INSERT INTO ORA9.BUDGET VALUES('0003','1101','2008',2400000);

1 개의 행이 만들어졌습니다.

SQL>

INSERT INTO ORA9.BUDGET VALUES('0003','1102','2009',3300000);

1 개의 행이 만들어졌습니다.

SQL>

INSERT INTO ORA9.BUDGET VALUES('0003','1102','2008',2700000);

1 개의 행이 만들어졌습니다.

SQL>

INSERT INTO ORA9.BUDGET VALUES('0004','1101','2009',4400000);

1 개의 행이 만들어졌습니다.

SQL>

INSERT INTO ORA9.BUDGET VALUES('0004','1101','2008',3800000);

1 개의 행이 만들어졌습니다.

SQL>

INSERT INTO ORA9.BUDGET VALUES('0004','1102','2009',2500000);

1 개의 행이 만들어졌습니다.

SQL>

INSERT INTO ORA9.BUDGET VALUES('0004','1102','2008',1800000);

1 개의 행이 만들어졌습니다.

SQL>

INSERT INTO ORA9.BUDGET VALUES('0005','1101','2009',3300000);

1 개의 행이 만들어졌습니다.

SQL>

INSERT INTO ORA9.BUDGET VALUES('0005','1101','2008',2900000);

1 개의 행이 만들어졌습니다.

SQL>

INSERT INTO ORA9.BUDGET VALUES('0005','1102','2009',3700000);

1 개의 행이 만들어졌습니다.

SQL>

INSERT INTO ORA9.BUDGET VALUES('0005','1102','2008',3000000);

1 개의 행이 만들어졌습니다.

SQL>

COMMIT;

커밋이 완료되었습니다.

5.결과

(1)SQL 분리시

SQL>

CREATE OR REPLACE PROCEDURE NOREL_JOIN_PRO1
2
3 (P_CUST_NO ORA9.CUSTOMER.CUST_NO%TYPE,
4 P_EMP_NO ORA9.EMPLOYEE.EMP_NO%TYPE,
5 P_DEPT_NO ORA9.BUDGET.DEPT_NO%TYPE
6 )
7
8 IS
9
10 CURSOR CUSTOMER_CSR IS
11 SELECT CUST_NAME
12 FROM ORA9.CUSTOMER
13 WHERE CUST_NO = P_CUST_NO;
14
15 CURSOR EMPLOYEE_CSR IS
16 SELECT DEPT
17 FROM ORA9.EMPLOYEE
18 WHERE EMP_NO = P_EMP_NO;
19
20 CURSOR BUDGET_CSR IS
21 SELECT BUDGET_AMT
22 FROM ORA9.BUDGET
23 WHERE DEPT_NO = P_DEPT_NO
24 AND ACCOUNT = '1101'
25 AND BUDGET_YR = TO_CHAR(SYSDATE, 'YYYY');
26
27 C_CUST_NM ORA9.CUSTOMER.CUST_NAME%TYPE;
28
29 C_DEPT ORA9.EMPLOYEE.DEPT%TYPE;
30
31 C_BUDGET_AMT ORA9.BUDGET.BUDGET_AMT%TYPE;
32
33 BEGIN
34
35 OPEN CUSTOMER_CSR;
36
37 LOOP
38
39 FETCH CUSTOMER_CSR INTO C_CUST_NM;
40
41 EXIT WHEN CUSTOMER_CSR%NOTFOUND;
42
43 DBMS_OUTPUT.PUT_LINE('고객명 : ' || C_CUST_NM);
44
45 OPEN EMPLOYEE_CSR;
46
47 LOOP
48
49 FETCH EMPLOYEE_CSR INTO C_DEPT;
50
51 EXIT WHEN EMPLOYEE_CSR%NOTFOUND;
52
53 DBMS_OUTPUT.PUT_LINE('부서 : ' || C_DEPT);
54
55 OPEN BUDGET_CSR;
56
57 LOOP
58
59 FETCH BUDGET_CSR INTO C_BUDGET_AMT;
60
61 EXIT WHEN BUDGET_CSR%NOTFOUND;
62
63 DBMS_OUTPUT.PUT_LINE('편성금액 : ' || C_BUDGET_AMT);
64
65 END LOOP;
66
67 CLOSE BUDGET_CSR;
68
69 END LOOP;
70
71 CLOSE EMPLOYEE_CSR;
72
73 END LOOP;
74
75 CLOSE CUSTOMER_CSR;
76
77 EXCEPTION
78
79 WHEN OTHERS THEN
80
81 DBMS_OUTPUT.PUT_LINE(SQLERRM||'에러 발생');
82
83 END;
84 /

프로시저가 생성되었습니다.

SQL>

SET SERVEROUTPUT ON;

SQL>

EXECUTE NOREL_JOIN_PRO1('0001','0001','0001');

고객명 : 고객1

부서 : 개발

편성금액 : 3000000

PL/SQL 처리가 정상적으로 완료되었습니다.

(2)SQL 통합시

SQL>

CREATE OR REPLACE PROCEDURE NOREL_JOIN_PRO2
2
3 (P_CUST_NO ORA9.CUSTOMER.CUST_NO%TYPE,
4 P_EMP_NO ORA9.EMPLOYEE.EMP_NO%TYPE,
5 P_DEPT_NO ORA9.BUDGET.DEPT_NO%TYPE
6 )
7
8 IS
9
10 CURSOR NOREL_JOIN_CSR IS
11 SELECT CUST_NAME
12 ,DEPT
13 ,BUDGET_AMT
14 FROM ORA9.CUSTOMER, ORA9.EMPLOYEE, ORA9.BUDGET
15 WHERE CUST_NO = P_CUST_NO
16 AND EMP_NO = P_EMP_NO
17 AND DEPT_NO = P_DEPT_NO
18 AND ACCOUNT = '1101'
19 AND BUDGET_YR = TO_CHAR(SYSDATE, 'YYYY');
20
21 C_CUST_NM ORA9.CUSTOMER.CUST_NAME%TYPE;
22
23 C_DEPT ORA9.EMPLOYEE.DEPT%TYPE;
24
25 C_BUDGET_AMT ORA9.BUDGET.BUDGET_AMT%TYPE;
26
27 BEGIN
28
29 OPEN NOREL_JOIN_CSR;
30
31 LOOP
32
33 FETCH NOREL_JOIN_CSR INTO C_CUST_NM,C_DEPT,C_BUDGET_AMT;
34
35 EXIT WHEN NOREL_JOIN_CSR%NOTFOUND;
36
37 DBMS_OUTPUT.PUT_LINE('고객명 : ' || C_CUST_NM);
38
39 DBMS_OUTPUT.PUT_LINE('부서 : ' || C_DEPT);
40
41 DBMS_OUTPUT.PUT_LINE('편성금액 : ' || C_BUDGET_AMT);
42
43 END LOOP;
44
45 CLOSE NOREL_JOIN_CSR;
46
47 EXCEPTION
48
49 WHEN OTHERS THEN
50
51 DBMS_OUTPUT.PUT_LINE(SQLERRM||'에러 발생');
52
53 END;
54 /

프로시저가 생성되었습니다.

SQL>

SET SERVEROUTPUT ON;

SQL>

EXECUTE NOREL_JOIN_PRO2('0001','0001','0001');

고객명 : 고객1

부서 : 개발

편성금액 : 3000000

PL/SQL 처리가 정상적으로 완료되었습니다.

(3)OUTER JOIN으로 SQL 통합시(실패)

SQL>

CREATE OR REPLACE PROCEDURE NOREL_JOIN_PRO3
2
3 (P_CUST_NO ORA9.CUSTOMER.CUST_NO%TYPE,
4 P_EMP_NO ORA9.EMPLOYEE.EMP_NO%TYPE,
5 P_DEPT_NO ORA9.BUDGET.DEPT_NO%TYPE
6 )
7
8 IS
9
10 CURSOR NOREL_JOIN_CSR IS
11 SELECT CUST_NAME
12 ,DEPT
13 ,BUDGET_AMT
14 FROM ORA9.CUSTOMER, ORA9.EMPLOYEE, ORA9.BUDGET
15 WHERE CUST_NO = P_CUST_NO
16 AND EMP_NO = P_EMP_NO
17 AND DEPT_NO(+) = P_DEPT_NO
18 AND ACCOUNT(+) = '1100'
19 AND BUDGET_YR(+) = TO_CHAR(SYSDATE, 'YYYY');
20
21 C_CUST_NM ORA9.CUSTOMER.CUST_NAME%TYPE;
22
23 C_DEPT ORA9.EMPLOYEE.DEPT%TYPE;
24
25 C_BUDGET_AMT ORA9.BUDGET.BUDGET_AMT%TYPE;
26
27 BEGIN
28
29 OPEN NOREL_JOIN_CSR;
30
31 LOOP
32
33 FETCH NOREL_JOIN_CSR INTO C_CUST_NM,C_DEPT,C_BUDGET_AMT;
34
35 EXIT WHEN NOREL_JOIN_CSR%NOTFOUND;
36
37 DBMS_OUTPUT.PUT_LINE('고객명 : ' || C_CUST_NM);
38
39 DBMS_OUTPUT.PUT_LINE('부서 : ' || C_DEPT);
40
41 DBMS_OUTPUT.PUT_LINE('편성금액 : ' || C_BUDGET_AMT);
42
43 END LOOP;
44
45 CLOSE NOREL_JOIN_CSR;
46
47 EXCEPTION
48
49 WHEN OTHERS THEN
50
51 DBMS_OUTPUT.PUT_LINE(SQLERRM||'에러 발생');
52
53 END;
54 /

프로시저가 생성되었습니다.

SQL>

SET SERVEROUTPUT ON;

SQL>

EXECUTE NOREL_JOIN_PRO3('0001','0001','0001');

PL/SQL 처리가 정상적으로 완료되었습니다. \--> 결과값이 출력되지 않았음

(4)OUTER JOIN으로 SQL 통합시(성공)

SQL>

CREATE OR REPLACE PROCEDURE NOREL_JOIN_PRO4
2
3 (P_CUST_NO ORA9.CUSTOMER.CUST_NO%TYPE,
4 P_EMP_NO ORA9.EMPLOYEE.EMP_NO%TYPE,
5 P_DEPT_NO ORA9.BUDGET.DEPT_NO%TYPE
6 )
7
8 IS
9
10 CURSOR NOREL_JOIN_CSR IS
11 SELECT CUST_NAME
12 ,DEPT
13 ,BUDGET_AMT
14 FROM ORA9.CUSTOMER, ORA9.EMPLOYEE, ORA9.BUDGET
15 WHERE CUST_NO = P_CUST_NO
16 AND EMP_NO = P_EMP_NO
17 AND DEPT_NO(+) = P_DEPT_NO || SUBSTR(EMP_NO,0,0)
18 AND ACCOUNT(+) = '1100'
19 AND BUDGET_YR(+) = TO_CHAR(SYSDATE, 'YYYY');
20
21 C_CUST_NM ORA9.CUSTOMER.CUST_NAME%TYPE;
22
23 C_DEPT ORA9.EMPLOYEE.DEPT%TYPE;
24
25 C_BUDGET_AMT ORA9.BUDGET.BUDGET_AMT%TYPE;
26
27 BEGIN
28
29 OPEN NOREL_JOIN_CSR;
30
31 LOOP
32
33 FETCH NOREL_JOIN_CSR INTO C_CUST_NM,C_DEPT,C_BUDGET_AMT;
34
35 EXIT WHEN NOREL_JOIN_CSR%NOTFOUND;
36
37 DBMS_OUTPUT.PUT_LINE('고객명 : ' || C_CUST_NM);
38
39 DBMS_OUTPUT.PUT_LINE('부서 : ' || C_DEPT);
40
41 DBMS_OUTPUT.PUT_LINE('편성금액 : ' || C_BUDGET_AMT);
42
43 END LOOP;
44
45 CLOSE NOREL_JOIN_CSR;
46
47 EXCEPTION
48
49 WHEN OTHERS THEN
50
51 DBMS_OUTPUT.PUT_LINE(SQLERRM||'에러 발생');
52
53 END;
54 /

프로시저가 생성되었습니다.

SQL>

SET SERVEROUTPUT ON;

SQL>

EXECUTE NOREL_JOIN_PRO4('0001','0001','0001');

고객명 : 고객1

부서 : 개발

편성금액 :

PL/SQL 처리가 정상적으로 완료되었습니다.

6. 주석

NOREL_JOIN_PRO3의 경우는 만족하는 값이 없으면 OUTER-JOIN 을 했다 하더라도 항상 공집합이다.

즉, DEPT_NO(+) = P_DEPT_NO 는 DEPT_NO = P_DEPT_NO 와 같은 결과를 낸다.

OUTER-JOIN 은 반드시 성공한 어떤 집합과 연결을 할 때만 의미가 있다.

즉, 상수값과는 OUTER-JOIN을 해도 결과는 달라지지 않는다.

반면 NOREL_JOIN_PRO4 에서는

CUST_NO = P_CUST_NO, EMP_NO = P_EMP_NO 를 만족하는 집합이 BUDGET 테이블과 OUTER-JOIN을 시도하는데,

DEPT_NO(+) = P_DEPT_NO || SUBSTR(EMP_NO,0,0) 조건을 보면

SUBSTR(EMP_NO,0,0)은 NULL 값이 되므로 P_DEPT_NO에 붙이더라도 그 결과에는 영향을 미치지 않는다.

이 구문의 추가로 인해 EMPLOYEE 테이블의 수행 결과는 BUDGET 테이블의 수행 결과에 영향을 받지 않는다.